# what is function Function is a block of code , which can be called multiple times.

In [1]:
# A function block always starts with a def keyword.
# A function has optional parameters.
# Every funtion has a return value.
# if there is no return value you get 'None'
# Basically,every function should have a return value.

def my_func():
    print "hello world"

In [2]:
print my_func()


hello world
None

In [5]:
# take 2
# return is a keyword which marks the end of the funtion.
# note: return is not a print statement.
def my_func():
    return "hello world"
    print "hey there"
    print "how is it today"
    print "hopefully it was a good monday"

In [6]:
print my_func()


hello world

In [7]:
# namespaces or local/global values.

In [8]:
# Nenu local
# variables defined inside a function are called local variables.
# The local variables are restricted to the function.
# there is no syntax which can access local variaable outside a function.
# The life span of a local variable is during the run time of the function.
def me_local():
    a = 10
    return a

In [9]:
print me_local()


10

In [10]:
print a #


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-10-d718eed2177d> in <module>()
----> 1 print a #

NameError: name 'a' is not defined

In [11]:
# locals()
def me_local():
    a = 10
    print locals()
    return a

In [13]:
print me_local()


{'a': 10}
10

In [14]:
# global
# first your function looks into local scope then into global scope.
# globals() is a function(inbuild) to show us global name spaces.

In [ ]:
'''
In [4]: x = 10

In [5]: def me_global():
   ...:     x = 2
   ...:     print locals()
   ...:     return x
   ...: 

In [6]: print me_global() # 2
{'x': 2}
2

In [7]: print x
10
'''

In [ ]:
'''
In [10]: globals()
Out[10]: 
{'In': ['',
  u'x = 10',
  u'def me_global():\n    x = 2\n    return x',
  u"get_ipython().magic(u'clear ')",
  u'x = 10',
  u'def me_global():\n    x = 2\n    print locals()\n    return x',
  u'print me_global() # 2',
  u'print x',
  u'print globals()',
  u'globals',
  u'globals()'],
 'Out': {9: <function globals>},
 '_': <function globals>,
 '_9': <function globals>,
 '__': '',
 '___': '',
 '__builtin__': <module '__builtin__' (built-in)>,
 '__builtins__': <module '__builtin__' (built-in)>,
 '__doc__': 'Automatically created module for IPython interactive environment',
 '__name__': '__main__',
 '_dh': [u'/home/khyaathi'],
 '_exit_code': 0,
 '_i': u'globals',
 '_i1': u'x = 10',
 '_i10': u'globals()',
 '_i2': u'def me_global():\n    x = 2\n    return x',
 '_i3': u'clear',
 '_i4': u'x = 10',
 '_i5': u'def me_global():\n    x = 2\n    print locals()\n    return x',
 '_i6': u'print me_global() # 2',
 '_i7': u'print x',
 '_i8': u'print globals()',
 '_i9': u'globals',
 '_ih': ['',
  u'x = 10',
  u'def me_global():\n    x = 2\n    return x',
  u"get_ipython().magic(u'clear ')",
  u'x = 10',
  u'def me_global():\n    x = 2\n    print locals()\n    return x',
  u'print me_global() # 2',
  u'print x',
  u'print globals()',
  u'globals',
  u'globals()'],
 '_ii': u'print globals()',
 '_iii': u'print x',
 '_oh': {9: <function globals>},
 '_sh': <module 'IPython.core.shadowns' from '/usr/local/lib/python2.7/dist-packages/IPython/core/shadowns.pyc'>,
 'exit': <IPython.core.autocall.ExitAutocall at 0x7f0b4fd386d0>,
 'get_ipython': <bound method TerminalInteractiveShell.get_ipython of <IPython.terminal.interactiveshell.TerminalInteractiveShell object at 0x7f0b4fd4a090>>,
 'me_global': <function __main__.me_global>,
 'quit': <IPython.core.autocall.ExitAutocall at 0x7f0b4fd386d0>,
 'x': 10}

'''

In [17]:
# take 3
# if there is no variables in local namespace , we will go to global namespace.
x = 10
def me_global():
    print locals()
    return x

In [16]:
print me_global()


{}
10

In [20]:
print globals()['x']


10

In [23]:
# global

balance = 0  # global
def my_deposit():
    print locals()
    balance = balance + 10000
    return balance

In [24]:
print my_deposit()


{}
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-24-87870cd62757> in <module>()
----> 1 print my_deposit()

<ipython-input-23-b12fdeb8d484> in my_deposit()
      4 def my_deposit():
      5     print locals()
----> 6     balance = balance + 10000
      7     return balance
      8 

UnboundLocalError: local variable 'balance' referenced before assignment

In [25]:
balance = 0  # global
def my_deposit():
    balance = 0
    print locals()
    balance = balance + 10000
    return balance

In [26]:
print my_deposit()


{'balance': 0}
10000

In [27]:
def my_withdraw():
    
    print locals()
    balance = balance - 3000
    return balance

In [28]:
print my_withdraw()


{}
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-28-13040dbf34bc> in <module>()
----> 1 print my_withdraw()

<ipython-input-27-83c8d02b4443> in my_withdraw()
      1 def my_withdraw():
      2     print locals()
----> 3     balance = balance - 3000
      4     return balance

UnboundLocalError: local variable 'balance' referenced before assignment

In [29]:
def my_withdraw():
    balance = 0
    print locals()
    balance = balance - 3000
    return balance

In [30]:
print my_withdraw()


{'balance': 0}
-3000

In [34]:
# use case
# A single variable across multiple function you need to use global keyword.
balance = 0  # global
def my_deposit():
    global balance
    print locals()
    balance = balance + 10000
    return balance
def my_withdraw():
    global balance
    print locals()
    balance = balance - 3000
    return balance

In [35]:
print my_deposit()
print balance


{}
10000
10000

In [36]:
print my_withdraw()
print balance


{}
7000
7000

In [37]:
# Functional arguments

In [38]:
def my_add(a,b):
    return a + b

In [41]:
# positional based.
print my_add(10,20)
print my_add("Linux","rocks")
print my_add("rocks","Linux")


30
Linuxrocks
rocksLinux

In [42]:
# key based.
print my_add(b='rocks',a='Linux')


Linuxrocks

In [44]:
# default
def tables(num,kumar=10):
    for value in range(1,kumar+1):
        print "{0:2d} * {1:2d} = {2:3d}".format(num,value,num*value)

In [46]:
tables(2)


 2 *  1 =   2
 2 *  2 =   4
 2 *  3 =   6
 2 *  4 =   8
 2 *  5 =  10
 2 *  6 =  12
 2 *  7 =  14
 2 *  8 =  16
 2 *  9 =  18
 2 * 10 =  20

In [47]:
tables(2,5)


 2 *  1 =   2
 2 *  2 =   4
 2 *  3 =   6
 2 *  4 =   8
 2 *  5 =  10

In [49]:
# http://cache.filehippo.com/img/ex/1125__putty1.png
def putty(hostname,port=22):
    pass

In [50]:
# putty(www.google.com) # port 22
# putty(www.google.com,23) # port 23

In [51]:
# *,**,*args,**kwargs

In [52]:
# *
def my_add(a,b):
    return a + b

In [53]:
print my_add(10,20)


30

In [4]:
my_values = [200,300]
my_values2 = [300,600,800]
# a = my_values[0]
# b = my_values[1]

print my_add(my_values)


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-7729f18c52c7> in <module>()
      4 # b = my_values[1]
      5 
----> 6 print my_add(my_values)

TypeError: my_add() takes exactly 2 arguments (1 given)

In [55]:
print my_add(*my_values)


500

In [57]:
print my_add(*my_values2)


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-57-1f235df59f30> in <module>()
----> 1 print my_add(*my_values2)

TypeError: my_add() takes exactly 2 arguments (3 given)

In [6]:
print my_add(*my_values2[0:2])


900

In [1]:
# **
def my_add(a,b):
    return a + b

In [8]:
my_values = {'a':10,'b':20}
my_values2 = {'a':20,'c':50}

In [9]:
print my_add(**my_values)
print my_add(**my_values2)


30
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-95bb97ea460a> in <module>()
      1 print my_add(**my_values)
----> 2 print my_add(**my_values2)

TypeError: my_add() got an unexpected keyword argument 'c'

In [15]:
# *args

print help(max)
print max(22,33)
print max(-1,0,-4)
print max(-22,-33,-45,-55,-21)


Help on built-in function max in module __builtin__:

max(...)
    max(iterable[, key=func]) -> value
    max(a, b, c, ...[, key=func]) -> value
    
    With a single iterable argument, return its largest item.
    With two or more arguments, return the largest argument.

None
33
0
-21

In [17]:
# args return a tuple.
def gmax(*args):
    return args
    
print gmax(22,33)
print gmax(-1,0,-4)
print gmax(-22,-33,-45,-55,-21)


(22, 33)
(-1, 0, -4)
(-22, -33, -45, -55, -21)

In [20]:
def gmax(*args):
    big = args[0]
    for value in args:
        if value > big:
            big = value
    return big

print gmax(22,33)
print gmax(-1,0,-4)
print gmax(-22,-33,-45,-55,-21)


33
0
-21

In [21]:
# **kwargs
# kwargs returns a dictionary.

def callme(**kwargs):
    return kwargs

print callme(name='kumar',gender='m')
print callme(name='kumar',maiden='vijaya')
print callme(loc='hyd',name='kumar',gender='m')


{'gender': 'm', 'name': 'kumar'}
{'name': 'kumar', 'maiden': 'vijaya'}
{'loc': 'hyd', 'name': 'kumar', 'gender': 'm'}

In [27]:
def callme(**kwargs):
        if 'name' in kwargs:
            print "my name is {}".format(kwargs['name'])
        if 'gender' in kwargs:
            print "my gender is {}".format(kwargs['gender'])
        if 'loc' in kwargs:
            print 'my location is {}'.format(kwargs['loc'])
        if 'maiden' in kwargs:
            print "my maiden name is {}".format(kwargs['maiden'])
        return "Thank you"
            
print callme(name='kumar',gender='m')
print callme(name='kumar',maiden='vijaya')
print callme(loc='hyd',name='kumar',gender='m')


my name is kumar
my gender is m
Thank you
my name is kumar
my maiden name is vijaya
Thank you
my name is kumar
my gender is m
my location is hyd
Thank you

In [28]:
# function within functions
# function lower is getting called during the runtime of function upper.
#

In [32]:
def upper():
    x = 1       #local to upper,global to lower
    def lower(): # local to upper
        return x
    return lower()
    
print upper() # 1


1

In [38]:
def upper():
    x = 1       #local to upper,global to lower
    def lower(): # local to upper
        return x
    return lower()

foo = upper()
print foo


1

In [33]:
# lower is defined withing the upper function.
print lower()


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-33-20045edbe1b3> in <module>()
      1 # lower is defined withing the upper function.
----> 2 print lower()

NameError: name 'lower' is not defined

In [34]:
# ex:fibonacci series in python

In [35]:
def my_new():
    pass

print type(my_new)
print my_new    # address of the function
print my_new()  # calling my function


<type 'function'>
<function my_new at 0x7f94210df398>
None

In [36]:
# function closures
# the variables(local/global) available during the defination of a function
# will be available even if we call them away from run time.

def upper():
    x = 1       #local to upper,global to lower
    def lower(): # local to upper
        return x
    return lower  # address of the lower.
    
# use case

foo = upper()
'''
    def lower(): # local to upper
        return x
'''
print foo,type(foo)


<function lower at 0x7f942995dc08> <type 'function'>

In [37]:
print foo()


1

In [39]:
# map,filter,lambda

In [40]:
print help(map)


Help on built-in function map in module __builtin__:

map(...)
    map(function, sequence[, sequence, ...]) -> list
    
    Return a list of the results of applying the function to the items of
    the argument sequence(s).  If more than one sequence is given, the
    function is called with an argument list consisting of the corresponding
    item of each sequence, substituting None for missing values when not all
    sequences have the same length.  If the function is None, return a list of
    the items of the sequence (or a list of tuples if more than one sequence).

None

In [41]:
def my_square(a):
    return a * a

In [42]:
print my_square(12)


144

In [44]:
print map(my_square,range(1,10,2))


[1, 9, 25, 49, 81]

In [45]:
# filter
print help(filter)


Help on built-in function filter in module __builtin__:

filter(...)
    filter(function or None, sequence) -> list, tuple, or string
    
    Return those items of sequence for which function(item) is true.  If
    function is None, return the items that are true.  If sequence is a tuple
    or string, return the same type, else return a list.

None

In [47]:
def my_even(a):
    if a % 2 == 0:
        return 'even'
    
print my_even(10) # truth of a function,valid output.
print my_even(9)  # false of a function,not valid output


even
None

In [48]:
print filter(my_even,range(1,11))


[2, 4, 6, 8, 10]

In [50]:
# use cases
print map(my_square,range(1,10,2))
print filter(my_square,range(1,10,2)) # None
print filter(my_even,range(1,11))
print map(my_even,range(1,11)) # ['none','even',...10times]


[1, 9, 25, 49, 81]
[1, 3, 5, 7, 9]
[2, 4, 6, 8, 10]
[None, 'even', None, 'even', None, 'even', None, 'even', None, 'even']

In [52]:
# lambda
# nameless functions
'''
def my_even(a):
    if a % 2 == 0:
        return 'even'

def my_square(a):
    return a * a
'''

#print map(my_square,range(1,10,2))
print map(lambda a:a*a,range(1,10,2))
#print filter(my_even,range(1,11))
print filter(lambda a:a%2==0,range(1,11))


[1, 9, 25, 49, 81]
[2, 4, 6, 8, 10]

In [ ]:
Reference: https://github.com/zhiwehu/Python-programming-exercises/blob/master/100%2B%20Python%20challenging%20programming%20exercises.txt